// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::structure::*;
use std::cmp::Ordering;

// TODO: deep_clone()
/// `NodeMethod` trait, which provides the most basic methods of `Node`.
///
/// Structure which implements this trait can be called `Node`.
///
/// Other methods of `Interface Node` in `XML DOM` are not available on all
/// `Node`s, so `Interface Node` is split into multiple traits. You can see
/// `ChildNodeMethod` `ParentNodeMethod` and `ValuedNodeMethod` for more
/// details.
///
/// # Association
/// [Interface Node](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1950641247)
pub trait NodeMethod {
    /// Attempt to convert special `Node`s into a general `Node` type so that
    /// they can be placed in a container. For example, you can convert
    /// `Document` and `Comment` into `Node`, so that you can add them to
    /// a `Vec` or other containers.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, NodeMethod};
    ///
    /// // Create a `Document` and then convert it into a `Node` type.
    /// let document = Document::new();
    /// let node1 = document.as_node();
    ///
    /// // Create a `Comment` and then convert it into a `Node` type.
    /// let comment = document.create_comment("Comment").unwrap();
    /// let node2 = comment.as_node();
    ///
    /// // Then we can add this two nodes to `Vec`.
    /// let vec = vec![node1, node2];
    /// ```
    fn as_node(&self) -> Node;

    /// The name of this `Node`, depending on its type.
    ///
    /// You can see the list below:
    ///
    /// | Interface             | nodeName                             |
    /// |-----------------------|--------------------------------------|
    /// | Attr                  | same as Attr.name                    |
    /// | CDATASection          | "#cdata-section"                     |
    /// | Comment               | "#comment"                           |
    /// | Document              | "#document"                          |
    /// | Element               | same as Element.name                 |
    /// | Text                  | "#text"                              |
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, NodeMethod};
    ///
    /// let document = Document::new();
    /// assert_eq!(document.name(), String::from("#document"));
    /// ```
    fn name(&self) -> String;

    /// Get the `Node`'s type.
    ///
    /// # Association
    /// [Node::nodeType](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-111237558)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, NodeMethod, NodeType};
    ///
    /// let document = Document::new();
    /// assert_eq!(document.node_type(), NodeType::Document);
    /// ```
    fn node_type(&self) -> NodeType;

    /// Get the `Document` associated with this `Node`. When this node is a
    /// `Document`, this method returns `None`.
    ///
    /// # Association
    /// [Node::ownerDocument](https://www.w3.org/TR/DOM-Level-3-Core/core.html#node-ownerDoc)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, NodeMethod};
    ///
    /// let document = Document::new();
    /// assert_eq!(document.owner_document(), None);
    /// let element = document.create_element("Element").unwrap();
    /// assert_eq!(element.owner_document(), Some(document));
    /// ```
    fn owner_document(&self) -> Option<Document>;
}

macro_rules! node {
    ($($inner: tt, $pat: ident, $into_f: ident);* $(;)?) => {
        /// `Node` structure. We can use it to wrap all special `Node`s.
        ///
        /// We can use `Trait Object` to convert a series of special nodes to a
        /// specific type, but we cannot convert from this `Trait Object` to a
        /// specific subtype. We need to provide a structure to contain all
        /// the special nodes in order to do this conversion.
        ///
        /// # Example
        /// ```
        /// use ylong_xml::{Document, NodeMethod};
        ///
        /// let document = Document::new();
        /// let node = document.as_node();
        /// ```
        #[derive(Debug, Clone)]
        pub enum Node {
            $(
                /// Variant of the Node enum.
                $pat($inner),
            )*
        }

        impl Node {
            $(
                /// Attempt to convert the node to a special node, return `Err`
                /// if the conversion fails.
                pub fn $into_f(self) -> Result<$inner, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }
            )*
        }

        impl NodeMethod for Node {
            fn as_node(&self) -> Node {
                match self {
                    $(
                        Self::$pat(n) => n.as_node(),
                    )*
                }
            }

            fn name(&self) -> String {
                match self {
                    $(
                        Self::$pat(n) => n.name(),
                    )*
                }
            }

            fn node_type(&self) -> NodeType {
                match self {
                    $(
                        Self::$pat(n) => n.node_type(),
                    )*
                }
            }

            fn owner_document(&self) -> Option<Document> {
                match self {
                    $(
                        Self::$pat(n) => n.owner_document(),
                    )*
                }
            }
        }

        #[cfg(test)]
        impl PartialEq for Node {
            fn eq(&self, other: &Self) -> bool {
                match (self, other) {
                    $(
                        (Self::$pat(a), Self::$pat(b)) => a == b,
                    )*
                    _ => false,
                }
            }
        }
    }
}

node!(
    Element, Element, into_element;
    Attribute, Attribute, into_attribute;
    Text, Text, into_text;
    CdataSection, CdataSection, into_cdata_section;
    Comment, Comment, into_comment;
    Document, Document, into_document;
);

// TODO: is_value_eq()
/// `ValuedNodeMethod` trait. Nodes with value inside need to implement this
/// trait. This trait contains some of the value-related methods of `Interface
/// Node`.
///
/// # Association
/// [Interface Node](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1950641247)
pub trait ValuedNodeMethod: NodeMethod {
    /// Get the value of this `Node`.
    ///
    /// # Association
    /// [Node::value](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-F68D080)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ValuedNodeMethod};
    ///
    /// let document = Document::new();
    /// let comment = document.create_comment("Comment").unwrap();
    /// assert_eq!(comment.value(), String::from("Comment"));
    /// ```
    fn value(&self) -> String;

    /// Set the value of this `Node`.
    ///
    /// # Association
    /// [Node::value](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-F68D080)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ValuedNodeMethod};
    ///
    /// let document = Document::new();
    /// let mut comment = document.create_comment("OldValue").unwrap();
    /// assert_eq!(comment.value(), String::from("OldValue"));
    /// comment.set_value("NewValue");
    /// assert_eq!(comment.value(), String::from("NewValue"));
    /// ```
    fn set_value(&mut self, value: &str);
}

/// `ChildNodeInternalMethod` trait, which includes some internal methods of
/// `ChildNode`.
pub trait ChildNodeInternalMethod: NodeMethod {
    /// Set the parent of `ChildNode`.
    fn set_parent(&mut self, parent: Option<WeakParentNode>);

    /// Check if the node is the same as the given `ChildNode`'s inner node.
    fn is_same_child_node(&self, node: &ChildNode) -> bool;
}

/// `ChildNodeMethod` trait, which provides methods for `ChildNode`s. Structure
/// which implements this trait can be called `ChildNode`. `ChildNodeMethod`
/// trait is used in association with the `ChildNode` type.
pub trait ChildNodeMethod: ChildNodeInternalMethod {
    /// Attempt to convert special `ChildNode`s into a general `ChildNode` type
    /// so that they can be placed in a container. For example, you can convert
    /// `Element` and `Comment` into `ChildNode`, then you can add them to
    /// a `Vec` or other containers.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{ChildNodeMethod, Document, DocumentMethod};
    ///
    /// // Create an `Element` and then convert it into `ChildNode`.
    /// let document = Document::new();
    /// let element = document.create_element("Element").unwrap();
    /// let child_node1 = element.as_child_node();
    ///
    /// // Create an `Comment` and then convert it into `ChildNode`.
    /// let comment = document.create_comment("Comment").unwrap();
    /// let child_node2 = comment.as_child_node();
    ///
    /// // Then you can add them to a `Vec`..
    /// let vec = vec![child_node1, child_node2];
    /// ```
    fn as_child_node(&self) -> ChildNode;

    /// The parent of this node. All nodes, except `Attr` and `Document`, may
    /// have a parent. However, if a node has just been created and not yet
    /// added to the tree, or if it has been removed from the tree, this method
    /// returns `None`.
    ///
    /// # Association
    /// [Node::parentNode](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1060184317)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ChildNodeMethod, ParentNodeMethod};
    ///
    /// let mut document = Document::new();
    /// let element = document.create_element("Element").unwrap();
    /// assert!(element.parent().is_none());
    /// assert!(document.append_child(element.clone()).is_ok());
    /// assert_eq!(element.parent(), Some(document.as_parent_node()));
    /// ```
    fn parent(&self) -> Option<ParentNode>;

    /// The node immediately preceding this node. If there is no such node,
    /// this returns `None`.
    ///
    /// # Association
    /// [Node::previousSibling](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-640FB3C8)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{ChildNodeMethod, Document, DocumentMethod, ParentNodeMethod};
    ///
    /// let mut document = Document::new();
    /// let comment1 = document.create_comment("Comment1").unwrap();
    /// let comment2 = document.create_comment("Comment2").unwrap();
    ///
    /// assert!(document.append_child(comment1.clone()).is_ok());
    /// assert!(document.append_child(comment2.clone()).is_ok());
    /// assert!(comment1.previous_sibling().is_none());
    /// assert_eq!(comment2.previous_sibling(), Some(comment1.as_child_node()));
    /// ```
    fn previous_sibling(&self) -> Option<ChildNode> {
        let parent = self.parent()?;
        let children = parent.children();
        let list = children.list.borrow();
        let pos = list.iter().position(|n| self.is_same_child_node(n))?;
        if pos == 0 {
            return None;
        }
        list.get(pos - 1).cloned()
    }

    /// The node immediately following this node. If there is no such node,
    /// this returns `None`.
    ///
    /// # Association
    /// [Node::nextSibling](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-6AC54C2F)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{ChildNodeMethod, Document, DocumentMethod, ParentNodeMethod};
    ///
    /// let mut document = Document::new();
    /// let comment1 = document.create_comment("Comment1").unwrap();
    /// let comment2 = document.create_comment("Comment2").unwrap();
    ///
    /// assert!(document.append_child(comment1.clone()).is_ok());
    /// assert!(document.append_child(comment2.clone()).is_ok());
    /// assert_eq!(comment1.next_sibling(), Some(comment2.as_child_node()));
    /// assert!(comment2.next_sibling().is_none());
    /// ```
    fn next_sibling(&self) -> Option<ChildNode> {
        let parent = self.parent()?;
        let children = parent.children();
        let list = children.list.borrow();
        let pos = list.iter().position(|n| self.is_same_child_node(n))?;
        list.get(pos + 1).cloned()
    }
}

macro_rules! child_node {
    ($($inner: tt, $pat: ident, $into_f: ident, $as_f: ident, $is_f: ident);* $(;)?) => {
        /// `ChildNode` structure. We can use it to wrap all special `ChildNode`s.
        ///
        /// We can use `Trait Object` to convert a series of special nodes to a
        /// specific type, but we cannot convert from this `Trait Object` to a
        /// specific subtype. We need to provide a structure to contain all
        /// the special nodes in order to do this conversion.
        ///
        /// # Example
        /// ```
        /// use ylong_xml::{ChildNodeMethod, Document, DocumentMethod, NodeMethod};
        ///
        /// let document = Document::new();
        /// let element = document.create_element("Element").unwrap();
        /// let child_node = element.as_child_node();
        /// ```
        #[derive(Debug, Clone)]
        pub enum ChildNode {
            $(
                /// Variant of the ChildNode enum.
                $pat($inner),
            )*
        }

        impl PartialEq for ChildNode {
            fn eq(&self, other: &Self) -> bool {
                match (self, other) {
                    $(
                        (Self::$pat(a), Self::$pat(b)) => a == b,
                    )*
                    _ => false,
                }
            }
        }

        impl ChildNode {
            $(
                /// Take the ownership, then attempt to convert itself to a
                /// special node, return `Err` if the conversion fails.
                pub fn $into_f(self) -> Result<$inner, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }

                /// Attempt to convert the node to a special node, return `Err`
                /// if the conversion fails.
                pub fn $as_f(&self) -> Result<&$inner, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }

                /// Check if it is a specified special `ChildNode`.
                pub fn $is_f(&self) -> bool {
                    matches!(self, Self::$pat(_))
                }
            )*
        }

        impl NodeMethod for ChildNode {
            fn as_node(&self) -> Node {
                match self {
                    $(
                        Self::$pat(n) => n.as_node(),
                    )*
                }
            }

            fn name(&self) -> String {
                match self {
                    $(
                        Self::$pat(n) => n.name(),
                    )*
                }
            }

            fn node_type(&self) -> NodeType {
                match self {
                    $(
                        Self::$pat(n) => n.node_type(),
                    )*
                }
            }

            fn owner_document(&self) -> Option<Document> {
                match self {
                    $(
                        Self::$pat(n) => n.owner_document(),
                    )*
                }
            }
        }

        impl ChildNodeInternalMethod for ChildNode {
            fn set_parent(&mut self, parent: Option<WeakParentNode>) {
                match self {
                    $(
                        Self::$pat(n) => n.set_parent(parent),
                    )*
                }
            }

            fn is_same_child_node(&self, node: &ChildNode) -> bool {
                self == node
            }
        }

        impl ChildNodeMethod for ChildNode {
            fn as_child_node(&self) -> ChildNode {
                self.clone()
            }

            fn parent(&self) -> Option<ParentNode> {
                match self {
                    $(
                        Self::$pat(n) => n.parent(),
                    )*
                }
            }
        }
    }
}

child_node!(
    Element, Element, into_element, as_element, is_element;
    Text, Text, into_text, as_text, is_text;
    Comment, Comment, into_comment, as_comment, is_comment;
    CdataSection, CdataSection, into_cdata_section,  as_cdata_section, is_cdata_section;
);

/// `ParentNodeInternalMethod` trait, which includes some internal methods of
/// `ParentNode`.
pub trait ParentNodeInternalMethod: NodeMethod {
    type WeakNode: WeakParentNodeMethod;

    /// Attempt to convert a special `ParentNode` to its weak type.
    fn downgrade(&self) -> Self::WeakNode;

    /// Attempt to convert a special `ParentNode` to `ChildNode` if the special
    /// `ParentNode` is also a special `ChildNode`. This method is mainly to
    /// solve the type conversion problem of  from `ParentNode` to `ChildNode`
    /// in `check_ancestor()` method.
    ///
    /// If the special `ParentNode` implements `ChildMethod`, then you will get
    /// a `ChildNode` of it, or you will get `None` if not.
    fn try_as_child_node(&self) -> Option<ChildNode>;

    /// Check if the given node is the allowed child type of this node.
    fn is_child_allowed(&self, node: &ChildNode) -> bool;

    /// Check if the given node has the same owner document as this node.
    fn is_same_document(&self, node: &ChildNode) -> bool {
        node.owner_document() == self.owner_document()
    }

    /// Check if the given node is an ancestor of this node.
    fn is_descendant_of(&self, node: &ChildNode) -> bool {
        if let Some(curr) = self.try_as_child_node() {
            if &curr == node {
                return true;
            }
            if let Some(parent) = curr.parent() {
                return parent.is_descendant_of(node);
            }
        }
        false
    }
}

/// `ParentNodeMethod` trait, which provides methods for `ParentNode`s.
/// Structure which implements this trait can be called `ParentNode`.
/// `ParentNodeMethod` trait is used in association with the `ParentNode` and
/// `WeakParentNode` type.
pub trait ParentNodeMethod: ParentNodeInternalMethod {
    /// Attempt to convert special `ParentNode`s into a general `ParentNode`
    /// type so that they can be placed in a container. For example, you can
    /// convert `Document` and `Element` into `ParentNode`, and then you can
    /// add them to a `Vec` or other containers.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ParentNodeMethod};
    ///
    /// // Create an `Document` and convert it into a `ParentNode`.
    /// let document = Document::new();
    /// // Create an `Element` and convert it into a `ParentNode`.
    /// let element = document.create_element("Element").unwrap();
    ///
    /// let parent_node1 = document.as_parent_node();
    /// let parent_node2 = document.as_parent_node();
    ///
    /// // Add them to a container..
    /// let vec = vec![parent_node1, parent_node2];
    /// ```
    fn as_parent_node(&self) -> ParentNode;

    /// A `NodeList` that contains all children of this node. If there are
    /// no children, an empty `NodeList` is returned.
    ///
    /// # Association
    /// [Node::childNodes](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1451460987)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, ParentNodeMethod};
    ///
    /// let document = Document::new();
    /// let children = document.children();
    /// assert_eq!(children.len(), 0);
    /// ```
    fn children(&self) -> NodeList<ChildNode>;

    /// Check if there are some child nodes in this node.
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, ParentNodeMethod};
    ///
    /// let document = Document::new();
    /// assert_eq!(document.has_children(), false);
    /// ```
    fn has_children(&self) -> bool {
        !self.children().is_empty()
    }

    /// Get the first child of this node. Return `None` if there is no such node.
    ///
    /// # Association
    /// [Node::firstChild](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-169727388)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, ParentNodeMethod};
    ///
    /// let document = Document::new();
    /// assert!(document.first_child().is_none());
    /// ```
    fn first_child(&self) -> Option<ChildNode> {
        self.children().first()
    }

    /// Get the last child of this node. Return `None` if there is no such node.
    ///
    /// # Association
    /// [Node::lastChild](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-61AD09FB)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, ParentNodeMethod};
    ///
    /// let document = Document::new();
    /// assert!(document.last_child().is_none());
    /// ```
    fn last_child(&self) -> Option<ChildNode> {
        self.children().last()
    }

    /// Insert the node `new_child` before the existing child node `ref_child`.
    /// If the `newChild` is already in the tree, it is first removed.
    ///
    /// # Errors
    /// * HIERARCHY_REQUEST_ERR:
    /// Raised if this node is of a type that does not allow children of the
    /// type of the `new_child` node, or if the node to insert is one of
    /// this node's ancestors or this node itself, or if this node is of
    /// type `Document` and user attempt to insert a second `Element` node.
    ///
    /// * WRONG_DOCUMENT_ERR:
    /// Raised if `new_child` was created from a different document than the one
    /// that created this node.
    ///
    /// * NOT_FOUND_ERR:
    /// Raised if `ref_child` is not a child of this node.
    ///
    /// # Differences
    /// * You cannot use this method without a `ref_child`.
    /// * Nothing will be returned if the operation was successful.
    ///
    /// # Association
    /// [Node::insertBefore](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-952280727)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ParentNodeMethod};
    ///
    /// // Create a document first.
    /// let mut document = Document::new();
    ///
    /// // We need two children.
    /// let comment1 = document.create_comment("NewChild1").unwrap();
    /// let comment2 = document.create_comment("NewChild2").unwrap();
    ///
    /// // Add the first child to the document.
    /// assert!(document.append_child(comment1.clone()).is_ok());
    /// // Then insert the second child before it.
    /// assert!(document.insert_before(comment2.clone(), &comment1).is_ok());
    /// // The document has two children now.
    /// assert_eq!(document.children().len(), 2);
    /// // The first child is `comment2`.
    /// assert_eq!(document.first_child().unwrap().as_comment().ok(), Some(&comment2));
    /// // The last child is `comment1`.
    /// assert_eq!(document.last_child().unwrap().as_comment().ok(), Some(&comment1));
    /// ```
    fn insert_before<A, B>(&mut self, new_child: A, ref_child: &B) -> Result<(), XmlError>
    where
        A: ChildNodeMethod,
        B: ChildNodeMethod,
    {
        let new_child = new_child.as_child_node();
        let ref_child = ref_child.as_child_node();

        // Check if `new_child` is an ancestor node, and check if `new_child`
        // is an allowed child node.
        if self.is_descendant_of(&new_child) || !self.is_child_allowed(&new_child) {
            return Err(DomException::HierarchyRequestErr.into());
        }
        // ..then check if `new_child` is from the same `Document` as `self`.
        if !self.is_same_document(&new_child) {
            return Err(DomException::WrongDocumentErr.into());
        }

        let children = self.children();
        let mut list_mut = children.list.borrow_mut();

        // Returns `NotFoundErr` if `ref_child` is not found.
        let mut insert_pos = list_mut
            .iter()
            .position(|n| n == &ref_child)
            .ok_or(DomException::NotFoundErr)?;

        // The same child node needs to be deleted before insertion.
        if let Some(remove_pos) = list_mut.iter().position(|n| n == &new_child) {
            // If we find it, then delete it.
            list_mut.remove(remove_pos);
            // Since the content of the list changes, the insertion position
            // needs to be changed.
            if insert_pos > remove_pos {
                insert_pos -= 1;
            }
            list_mut.insert(insert_pos, new_child);
        } else {
            list_mut.insert(insert_pos, new_child);
            // Set parent of the inserted node as the current node.
            list_mut[insert_pos].set_parent(Some(self.as_parent_node().downgrade()));
        }
        Ok(())
    }

    /// Replace the child node `old_child` with `new_child` in the list of
    /// children. If the `new_child` is already in the tree, it is first
    /// removed.
    ///
    /// # Errors
    /// * HIERARCHY_REQUEST_ERR:
    /// Raised if this node is of a type that does not allow children of the
    /// type of the `new_child` node, or if the node to put in is one of
    /// this node's ancestors or this node itself, or if this node is of
    /// type `Document` and the result of the replacement operation would
    /// add a second `Element` on the `Document` node.
    ///
    /// * WRONG_DOCUMENT_ERR:
    /// Raised if `new_child` was created from a different document than the one
    /// that created this node.
    ///
    /// * NOT_FOUND_ERR:
    /// Raised if `old_child` is not a child of this node.
    ///
    /// # Differences
    /// * Nothing will be returned if the operation was successful.
    ///
    /// # Association
    /// [Node::replaceChild](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-785887307)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ParentNodeMethod};
    ///
    /// // Create a document first.
    /// let mut document = Document::new();
    ///
    /// // We need two children.
    /// let comment1 = document.create_comment("NewChild1").unwrap();
    /// let comment2 = document.create_comment("NewChild2").unwrap();
    ///
    /// // Add the first child to the document.
    /// assert!(document.append_child(comment1.clone()).is_ok());
    /// // Then replace it with `comment2`.
    /// assert!(document.replace_child(comment2.clone(), &comment1).is_ok());
    /// // The document has only one child.
    /// assert_eq!(document.children().len(), 1);
    /// // The first child is `comment2`.
    /// assert_eq!(document.first_child().unwrap().as_comment().ok(), Some(&comment2));
    /// // ..and also the last.
    /// assert_eq!(document.last_child().unwrap().as_comment().ok(), Some(&comment2));
    /// ```
    fn replace_child<A, B>(&mut self, new_child: A, old_child: &B) -> Result<(), XmlError>
    where
        A: ChildNodeMethod,
        B: ChildNodeMethod,
    {
        let new_child = new_child.as_child_node();
        let old_child = old_child.as_child_node();

        // Check if `new_child` is an ancestor node,
        // and check if `new_child` is an allowed child node.
        if self.is_descendant_of(&new_child) || !self.is_child_allowed(&new_child) {
            return Err(DomException::HierarchyRequestErr.into());
        }
        // ..then check if `new_child` is from the same `Document` as `self`.
        if !self.is_same_document(&new_child) {
            return Err(DomException::WrongDocumentErr.into());
        }

        let children = self.children();
        let mut list_mut = children.list.borrow_mut();

        // Returns `NotFoundErr` if `old_child` is not found.
        let replace_pos = list_mut
            .iter()
            .position(|n| n == &old_child)
            .ok_or(DomException::NotFoundErr)?;

        // Set parent of the replaced node to be `None`.
        list_mut[replace_pos].set_parent(None);

        // The same child node needs to be deleted before insertion.
        if let Some(remove_pos) = list_mut.iter().position(|n| n == &new_child) {
            list_mut.remove(remove_pos);

            match replace_pos.cmp(&remove_pos) {
                Ordering::Greater => *list_mut.get_mut(replace_pos - 1).unwrap() = new_child,
                Ordering::Equal => list_mut.insert(replace_pos, new_child),
                Ordering::Less => *list_mut.get_mut(replace_pos).unwrap() = new_child,
            }
        } else {
            let target = &mut list_mut[replace_pos];
            *target = new_child;
            // Set parent of the inserted node to be the current node.
            target.set_parent(Some(self.as_parent_node().downgrade()));
        }

        Ok(())
    }

    /// Remove the child node indicated by `old_child` from the list of
    /// children.
    ///
    /// # Errors
    /// * NOT_FOUND_ERR:
    /// Raised if `old_child` is not a child of this node.
    ///
    /// # Differences
    /// * Nothing will be returned if the operation was successful.
    ///
    /// # Association
    /// [Node::removeChild](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1734834066)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{ChildNodeMethod, Document, DocumentMethod, ParentNodeMethod};
    ///
    /// // Create a document first.
    /// let mut document = Document::new();
    ///
    /// // We need one child node.
    /// let comment = document.create_comment("NewChild").unwrap();
    /// // This node has no parent now.
    /// assert!(comment.parent().is_none());
    ///
    /// // Add the child to the document.
    /// assert!(document.append_child(comment.clone()).is_ok());
    /// // Now it has a parent.
    /// assert!(comment.parent().is_some());
    /// // Then remove it.
    /// assert!(document.remove_child(&comment).is_ok());
    /// // It has no parent again.
    /// assert!(comment.parent().is_none());
    /// ```
    fn remove_child<A>(&mut self, old_child: &A) -> Result<(), XmlError>
    where
        A: ChildNodeMethod,
    {
        let old_child = old_child.as_child_node();

        let children = self.children();
        let mut list_mut = children.list.borrow_mut();

        // Returns `NotFoundErr` if `old_child` is not found.
        let remove_pos = list_mut
            .iter()
            .position(|n| n == &old_child)
            .ok_or(DomException::NotFoundErr)?;

        // Set parent of the removed node to be `None`.
        list_mut[remove_pos].set_parent(None);
        list_mut.remove(remove_pos);
        Ok(())
    }

    /// Add the node `new_child` to the end of the list of children of this
    /// node. If the `new_child` is already in the tree, it is first
    /// removed.
    ///
    /// # Errors
    /// * HIERARCHY_REQUEST_ERR:
    /// Raised if this node is of a type that does not allow children of the
    /// type of the `new_child` node, or if the node to append is one of
    /// this node's ancestors or this node itself, or if this node is of
    /// type `Document` and the user attempts to append a second `Element`
    /// node.
    ///
    /// * WRONG_DOCUMENT_ERR:
    /// Raised if `new_child` was created from a different document than the one
    /// that created this node.
    ///
    /// # Association
    /// [Node::appendChild](https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-184E7107)
    ///
    /// # Example
    /// ```
    /// use ylong_xml::{Document, DocumentMethod, ParentNodeMethod};
    ///
    /// // Create a document first.
    /// let mut document = Document::new();
    ///
    /// // We need one child node.
    /// let comment = document.create_comment("NewChild").unwrap();
    ///
    /// // Add the child to the document.
    /// assert!(document.append_child(comment).is_ok());
    /// // Now the document has one child.
    /// assert_eq!(document.children().len(), 1);
    /// ```
    fn append_child<A>(&mut self, new_child: A) -> Result<(), XmlError>
    where
        A: ChildNodeMethod,
    {
        let new_child = new_child.as_child_node();

        // Check if `new_child` is an ancestor node,
        // and check if `new_child` is an allowed child node.
        if self.is_descendant_of(&new_child) || !self.is_child_allowed(&new_child) {
            return Err(DomException::HierarchyRequestErr.into());
        }
        // ..then check if `new_child` is from the same `Document` as `self`.
        if !self.is_same_document(&new_child) {
            return Err(DomException::WrongDocumentErr.into());
        }

        let children = self.children();
        let mut list_mut = children.list.borrow_mut();

        if let Some(remove_pos) = list_mut.iter().position(|n| n == &new_child) {
            list_mut.remove(remove_pos);
            list_mut.push(new_child);
        } else {
            list_mut.push(new_child);
            let len = list_mut.len();
            // Set parent of the inserted node to be the current node.
            list_mut[len - 1].set_parent(Some(self.as_parent_node().downgrade()));
        }
        Ok(())
    }
}

/// `WeakParentNodeMethod` trait, which provides methods for `ParentNode`'s
/// corresponding `Weak` type.
pub trait WeakParentNodeMethod {
    type Node: ParentNodeInternalMethod;

    /// Attempt to convert a special `WeakParentNode` to `ParentNode`.
    fn upgrade(&self) -> Option<Self::Node>;

    /// Attempt to convert a special `WeakParentNode` to a general
    /// `WeakParentNode` type.
    fn as_weak_parent_node(&self) -> WeakParentNode;
}

macro_rules! parent_node {
    ($($inner: tt, $weak_i: tt, $pat: ident, $into_f: ident, $as_f: ident, $is_f: ident);* $(;)?) => {
        /// `ParentNode` structure. We can use it to wrap all special `ParentNode`s.
        ///
        /// We can use `Trait Object` to convert a series of special nodes to a
        /// specific type, but we cannot convert from this `Trait Object` to a
        /// specific subtype. We need to provide a structure to contain all
        /// the special nodes in order to do this conversion.
        ///
        /// # Example
        /// ```
        /// use ylong_xml::{Document, ParentNodeMethod};
        ///
        /// let document = Document::new();
        /// let parent_node = document.as_parent_node();
        /// ```
        #[derive(Debug, Clone)]
        pub enum ParentNode {
            $(
                $pat($inner),
            )*
        }

        impl ParentNode {
            $(
                /// Take the ownership, attempt to convert itself to a
                /// special node, and then return `Err` if the conversion fails.
                pub fn $into_f(self) -> Result<$inner, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }

                /// Attempt to convert the node to a special node, return `Err`
                /// if the conversion fails.
                pub fn $as_f(&self) -> Result<&$inner, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }

                /// Check if it is a specified special `ChildNode`.
                pub fn $is_f(&self) -> bool {
                    matches!(self, Self::$pat(_))
                }
            )*
        }

        impl NodeMethod for ParentNode {
            fn as_node(&self) -> Node {
                match self {
                    $(
                        Self::$pat(n) => n.as_node(),
                    )*
                }
            }

            fn name(&self) -> String {
                match self {
                    $(
                        Self::$pat(n) => n.name(),
                    )*
                }
            }

            fn node_type(&self) -> NodeType {
                match self {
                    $(
                        Self::$pat(n) => n.node_type(),
                    )*
                }
            }

            fn owner_document(&self) -> Option<Document> {
                match self {
                    $(
                        Self::$pat(n) => n.owner_document(),
                    )*
                }
            }
        }

        impl ParentNodeInternalMethod for ParentNode {
            type WeakNode = WeakParentNode;

            fn downgrade(&self) -> Self::WeakNode {
                match self {
                    $(
                        Self::$pat(n) => n.downgrade().as_weak_parent_node(),
                    )*
                }
            }

            fn try_as_child_node(&self) -> Option<ChildNode> {
                match self {
                    $(
                        Self::$pat(n) => n.try_as_child_node(),
                    )*
                }
            }

            fn is_child_allowed(&self, node: &ChildNode) -> bool {
                match self {
                    $(
                        Self::$pat(n) => n.is_child_allowed(node),
                    )*
                }
            }
        }

        impl ParentNodeMethod for ParentNode {
            fn as_parent_node(&self) -> ParentNode {
                self.clone()
            }

            fn children(&self) -> NodeList<ChildNode> {
                match self {
                    $(
                        Self::$pat(n) => n.children(),
                    )*
                }
            }
        }

        impl PartialEq for ParentNode {
            fn eq(&self, other: &Self) -> bool {
                match (self, other) {
                    $(
                        (Self::$pat(a), Self::$pat(b)) => a == b,
                    )*
                    _ => false,
                }
            }
        }

        #[derive(Debug, Clone)]
        pub enum WeakParentNode {
            $(
                $pat($weak_i),
            )*
        }

        impl WeakParentNode {
            $(
                /// Take the ownership, then attempt to convert itself to a
                /// special node, return `Err` if the conversion fails.
                pub fn $into_f(self) -> Result<$weak_i, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }

                /// Attempt to convert itself to a special node, return `Err`
                /// if the conversion fails.
                pub fn $as_f(&self) -> Result<&$weak_i, XmlError> {
                    match self {
                        Self::$pat(n) => Ok(n),
                        _ => Err(OtherError::Transform.into()),
                    }
                }

                /// Check if it is a specified special `ChildNode`.
                pub fn $is_f(&self) -> bool {
                    matches!(self, Self::$pat(_))
                }
            )*
        }

        impl WeakParentNodeMethod for WeakParentNode {
            type Node = ParentNode;

            fn upgrade(&self) -> Option<Self::Node> {
                match self {
                    $(
                        Self::$pat(n) => n.upgrade().map(|n| n.as_parent_node()),
                    )*
                }
            }

            fn as_weak_parent_node(&self) -> WeakParentNode {
                self.clone()
            }
        }

        impl PartialEq for WeakParentNode {
            fn eq(&self, other: &Self) -> bool {
                match (self, other) {
                    $(
                        (Self::$pat(a), Self::$pat(b)) => a == b,
                    )*
                    _ => false,
                }
            }
        }
    }
}

parent_node!(
    Document, WeakDocument, Document, into_document, as_document, is_document;
    Element, WeakElement, Element, into_element, as_element, is_element;
);

#[cfg(test)]
mod ut_node {
    use crate::structure::*;

    /// UT test case for `ChildNodeMethod::previous_sibling()`.
    ///
    /// # Brief
    /// 1. Create a `Document` called `document`.
    /// 2. Create two `Comments` called `comment1` and `comment2`.
    /// 3. Add two `Comments` to `Document`.
    /// 4. Call `previous_sibling()` method to check if `comment2`'s previous sibling is `comment1`.
    #[test]
    fn ut_child_node_method_previous_sibling() {
        let mut document = Document::new();
        let comment1 = document.create_comment("Comment").unwrap();
        // At this point `comment1` has no parent node.
        assert_eq!(ChildNodeMethod::previous_sibling(&comment1), None);

        let _ = document.append_child(comment1.clone());
        // At this point `comment1` is a child of `document`, but `comment1` has no siblings.
        assert_eq!(ChildNodeMethod::previous_sibling(&comment1), None);

        let comment2 = document.create_comment("Comment").unwrap();
        let _ = document.append_child(comment2.clone());
        // At this point `comment2`'s previous sibling is `comment1`.
        assert_eq!(
            ChildNodeMethod::previous_sibling(&comment2),
            Some(comment1.as_child_node())
        );
    }

    /// UT test case for `ChildNodeMethod::next_sibling()`.
    ///
    /// # Brief
    /// 1. Create a `Document` called `document`.
    /// 2. Create two `Comments` called `comment1` and `comment2`.
    /// 3. Add two `Comments` to `Document`.
    /// 4. Call `next_sibling()` method to check if `comment1`'s next sibling is `comment2`.
    #[test]
    fn ut_child_node_method_next_sibling() {
        let mut document = Document::new();
        let comment1 = document.create_comment("Comment").unwrap();
        // At this point `comment1` has no parent node.
        assert_eq!(ChildNodeMethod::next_sibling(&comment1), None);

        let _ = document.append_child(comment1.clone());
        // At this point `comment1` is a child of `document`, but `comment1` has no siblings.
        assert_eq!(ChildNodeMethod::next_sibling(&comment1), None);

        let comment2 = document.create_comment("Comment").unwrap();
        let _ = document.append_child(comment2.clone());
        // At this point `comment2`'s previous sibling is `comment1`.
        assert_eq!(
            ChildNodeMethod::next_sibling(&comment1),
            Some(comment2.as_child_node())
        );
    }

    /// UT test case for `ParentNodeInternalMethod::is_same_document()`.
    ///
    /// # Brief
    /// 1. Create two `Document`s called `document1` and `document2`.
    /// 2. Create a `Comment` of `document1` called `comment1`, and create a
    /// `Comment` of `document2` called `comment2`.
    /// 3. Call `is_same_document()` method to check the relationship of these
    ///  objects.
    #[test]
    fn ut_parent_node_internal_method_is_same_document() {
        let document1 = Document::new();
        let comment1 = document1.create_comment("Comment").unwrap();
        let document2 = Document::new();
        let comment2 = document2.create_comment("Comment").unwrap();

        assert!(ParentNodeInternalMethod::is_same_document(
            &document1,
            &comment1.as_child_node()
        ));
        assert!(!ParentNodeInternalMethod::is_same_document(
            &document1,
            &comment2.as_child_node()
        ));
    }

    /// UT test case for `ParentNodeInternalMethod::is_descendant_of()`.
    ///
    /// # Brief
    /// 1. Create a `Document` called `document`.
    /// 2. Create two `Element`s called `element1` and `element2`.
    /// 3. Add `element2` to `element1`, then add `element1` to `document`.
    /// 4. Call `is_descendant_of` method to check the relationship between these
    /// objects.
    #[test]
    fn ut_parent_node_internal_method_is_descendant_of() {
        let mut document = Document::new();
        let mut element1 = document.create_element("Element").unwrap();
        let element2 = document.create_element("Element").unwrap();

        assert!(element1.append_child(element2.clone()).is_ok());
        assert!(document.append_child(element1.clone()).is_ok());
        assert!(!document.is_descendant_of(&element2.as_child_node()));
        assert!(element2.is_descendant_of(&element1.as_child_node()))
    }

    // TODO: ParentNodeMethod test.
}
